#include "stdafx.h"
#include "net/MailStream.hpp"

NET_NAMESPACE_BEGIN

CMailStreamBuf::CMailStreamBuf(std::istream& istr)
	: m_pIstr(&istr), 
	m_pOstr(0),
	m_state(ST_CR_LF)
{
}

CMailStreamBuf::CMailStreamBuf(std::ostream& ostr)
	: m_pIstr(0), 
	m_pOstr(&ostr),
	m_state(ST_CR_LF)
{
}

CMailStreamBuf::~CMailStreamBuf()
{
}

void CMailStreamBuf::close()
{
	if (m_pOstr && m_state != ST_CR_LF_DOT_CR_LF)
	{
		if (!m_buffer.empty())
			m_pOstr->write(m_buffer.data(), (std::streamsize) m_buffer.length());
		if (m_state != ST_CR_LF)
			m_pOstr->write("\r\n", 2);
		m_pOstr->write(".\r\n", 3);
		m_state = ST_CR_LF_DOT_CR_LF;
	}
}

int CMailStreamBuf::readFromDevice()
{
	int c = std::char_traits<char>::eof();
	if (!m_buffer.empty())
	{
		c = m_buffer[0];
		m_buffer.erase(0, 1);
	}
	else
	{
		c = readOne();
		while (c != std::char_traits<char>::eof() && m_state != ST_DATA && m_state != ST_CR_LF_DOT_CR_LF)
			c = readOne();
		if (!m_buffer.empty())
		{
			c = m_buffer[0];
			m_buffer.erase(0, 1);
		}
	}
	return c;
}

int CMailStreamBuf::readOne()
{
	int c = std::char_traits<char>::eof();
	if (m_state != ST_CR_LF_DOT_CR_LF)
	{
		c = m_pIstr->get();
		switch (c)
		{
		case '\r':
			if (m_state == ST_CR_LF_DOT)
				m_state = ST_CR_LF_DOT_CR;
			else
				m_state = ST_CR;
			break;
		case '\n':
			if (m_state == ST_CR)
				m_state = ST_CR_LF;
			else if (m_state == ST_CR_LF_DOT_CR)
				m_state = ST_CR_LF_DOT_CR_LF;
			else
				m_state = ST_DATA;
			break;
		case '.':
			if (m_state == ST_CR_LF)
				m_state = ST_CR_LF_DOT;
			else if (m_state == ST_CR_LF_DOT)
				m_state = ST_CR_LF_DOT_DOT;
			else
				m_state = ST_DATA;
			break;
		default:
			m_state = ST_DATA;
		}
		if (m_state == ST_CR_LF_DOT_DOT)
			m_state = ST_DATA;
		else if (m_state == ST_CR_LF_DOT_CR_LF)
			m_buffer.resize(m_buffer.size() - 2);
		else if (c != std::char_traits<char>::eof())
			m_buffer += (char) c;
	}
	return c;
}

int CMailStreamBuf::writeToDevice(char c)
{
	switch (c)
	{
	case '\r':
		m_state = ST_CR;
		break;
	case '\n':
		if (m_state == ST_CR)
			m_state = ST_CR_LF;
		else
			m_state = ST_DATA;
		break;
	case '.':
		if (m_state == ST_CR_LF)
			m_state = ST_CR_LF_DOT;
		else
			m_state = ST_DATA;
		break;
	default:
		m_state = ST_DATA;
	}
	if (m_state == ST_DATA)
	{
		if (!m_buffer.empty())
		{
			m_pOstr->write(m_buffer.data(), (std::streamsize) m_buffer.length());
			m_buffer.clear();
		}
		m_pOstr->put(c);
	}
	else if (m_state == ST_CR_LF_DOT)
	{
		// buffer contains one or more CR-LF pairs
		m_pOstr->write(m_buffer.data(), (std::streamsize) m_buffer.length());
		m_pOstr->write("..", 2);
		m_state = ST_DATA;
		m_buffer.clear();
	}
	else m_buffer += c;
	return charToInt(c);
}


CMailIOS::CMailIOS(std::istream& istr) : m_buf(istr)
{
}


CMailIOS::CMailIOS(std::ostream& ostr) : m_buf(ostr)
{
	
}

CMailIOS::~CMailIOS()
{
}

void CMailIOS::close()
{
	m_buf.close();
}

CMailStreamBuf* CMailIOS::rdbuf()
{
	return &m_buf;
}


CMailInputStream::CMailInputStream(std::istream& istr)
	: CMailIOS(istr), 
	std::istream(&m_buf)
{
}

CMailInputStream::~CMailInputStream()
{
}


CMailOutputStream::CMailOutputStream(std::ostream& ostr)
	: CMailIOS(ostr), 
	std::ostream(&m_buf)
{
}

CMailOutputStream::~CMailOutputStream()
{
}

NET_NAMESPACE_END
